package fi.bluepill.server.rest;

import fi.bluepill.server.AuthenticationException;
import fi.bluepill.server.model.UserAccount;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.node.ObjectNode;
import org.owasp.esapi.ESAPI;
import org.owasp.esapi.crypto.CipherText;
import org.owasp.esapi.crypto.PlainText;
import org.owasp.esapi.errors.AccessControlException;
import org.owasp.esapi.errors.EncryptionException;
import org.owasp.esapi.reference.DefaultHTTPUtilities;
import org.scribe.builder.ServiceBuilder;
import org.scribe.builder.api.FacebookApi;
import org.scribe.model.*;
import org.scribe.oauth.OAuthService;
import rip.core.Transactional;

import javax.servlet.http.Cookie;
import java.io.IOException;

public class FacebookService extends BluepillService {
    public void login() throws IOException, AccessControlException {
        assertSecureChannel();

        OAuthService service = new ServiceBuilder()
                .provider(FacebookApi.class)
                .apiKey(env("facebook.api.key"))
                .apiSecret(env("facebook.api.secret"))
                .callback(env("facebook.callback.url"))
                .scope("email")
                .build();

        String authorizationUrl = service.getAuthorizationUrl(null);

        response.sendRedirect(authorizationUrl);

    }

    @Transactional
    public void callback() throws IOException, AccessControlException {
        assertSecureChannel();

        OAuthService service = new ServiceBuilder()
                .provider(FacebookApi.class)
                .apiKey(env("facebook.api.key"))
                .apiSecret(env("facebook.api.secret"))
                .callback(env("facebook.callback.url"))
                .scope("email")
                .build();

        Verifier verifier = new Verifier(request.getParameter("code"));
        Token accessToken = service.getAccessToken(null, verifier);
        OAuthRequest request = new OAuthRequest(Verb.GET, "https://graph.facebook.com/me");
        service.signRequest(accessToken, request);
        Response response = request.send();

        JsonNode responseJson = json().readTree(response.getBody());

        UserAccount user =  user(responseJson.get("id").asText());

        if (user == null) {
            user = new UserAccount();
            user.setUsername(responseJson.get("username").asText());
            user.setExternalUid(responseJson.get("id").asText());
            user.setEmail(responseJson.get("email").asText());
            user.setFirstName(responseJson.get("first_name").asText());
            user.setLastName(responseJson.get("last_name").asText());

            jpa().persist(user);
        }

        user.getKeystore().setFacebookAccessToken(accessToken.getToken());
        jpa().merge(user);

        jpa().detach(user);


        long expires = System.currentTimeMillis() + (14 * 24 * 60 * 60 * 1000); //14 days

        ObjectNode authCookieJson = json().createObjectNode();
        authCookieJson.put("expires", expires);
        authCookieJson.put("uuid", user.getUuid());
        authCookieJson.put("ip", ipAddress());

        String encryptedState = null;

        try {
            PlainText plainText = new PlainText(authCookieJson.toString());
            CipherText cipherText = ESAPI.encryptor().encrypt(plainText);
            encryptedState = ESAPI.encoder().encodeForBase64(cipherText.asPortableSerializedByteArray(), false);
        } catch (EncryptionException e) {
            throw new AuthenticationException("Could not encrypt cookie", e);
        }

        Cookie authCookie = new Cookie("bluepillAuth", encryptedState);
        authCookie.setDomain(env("domain"));
        authCookie.setMaxAge((int) ((expires - System.currentTimeMillis()) / 1000));

        if (!isTestMode())
            authCookie.setSecure(true);

        authCookie.setPath("/");

        DefaultHTTPUtilities.getInstance().addCookie(super.response, authCookie);

        ObjectNode m = okMessage();

        write(m);
    }
}
